home *** CD-ROM | disk | FTP | other *** search
/ Programming an RTS Game with Direct3D / Programming an RTS Game with Direct3D.iso / Examples / Chapter 15 / Example 15.2 / terrain.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2006-07-17  |  27.7 KB  |  1,031 lines

  1. #include "terrain.h"
  2. #include "camera.h"
  3.  
  4. const DWORD TERRAINVertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX2;
  5.  
  6. //////////////////////////////////////////////////////////////////////////////////////////
  7. //                                    PATCH                                                //
  8. //////////////////////////////////////////////////////////////////////////////////////////
  9.  
  10. PATCH::PATCH()
  11. {
  12.     m_pDevice = NULL;
  13.     m_pMesh = NULL;
  14. }
  15. PATCH::~PATCH()
  16. {
  17.     Release();
  18. }
  19.  
  20. void PATCH::Release()
  21. {
  22.     if(m_pMesh != NULL)
  23.         m_pMesh->Release();
  24.     m_pMesh = NULL;
  25. }
  26.  
  27. HRESULT PATCH::CreateMesh(TERRAIN &ter, RECT source, IDirect3DDevice9* Dev)
  28. {
  29.     if(m_pMesh != NULL)
  30.     {
  31.         m_pMesh->Release();
  32.         m_pMesh = NULL;
  33.     }
  34.  
  35.     try
  36.     {
  37.         m_pDevice = Dev;
  38.         m_mapRect = source;
  39.  
  40.         int width = source.right - source.left;
  41.         int height = source.bottom - source.top;
  42.         int nrVert = (width + 1) * (height + 1);
  43.         int nrTri = width * height * 2;
  44.  
  45.         if(FAILED(D3DXCreateMeshFVF(nrTri, nrVert, D3DXMESH_MANAGED, TERRAINVertex::FVF, m_pDevice, &m_pMesh)))
  46.         {
  47.             debug.Print("Couldn't create mesh for PATCH");
  48.             return E_FAIL;
  49.         }
  50.  
  51.         m_BBox.max = D3DXVECTOR3(-10000.0f, -10000.0f, -10000.0f);
  52.         m_BBox.min = D3DXVECTOR3(10000.0f, 10000.0f, 10000.0f);
  53.  
  54.         //Create vertices
  55.         TERRAINVertex* ver = 0;
  56.         m_pMesh->LockVertexBuffer(0,(void**)&ver);
  57.         for(int z=source.top, z0 = 0;z<=source.bottom;z++, z0++)
  58.             for(int x=source.left, x0 = 0;x<=source.right;x++, x0++)
  59.             {
  60.                 MAPTILE *tile = ter.GetTile(x, z);
  61.  
  62.                 D3DXVECTOR3 pos = D3DXVECTOR3(x, tile->m_height, -z);
  63.                 D3DXVECTOR2 alphaUV = D3DXVECTOR2(x / (float)ter.m_size.x, z / (float)ter.m_size.y);        //Alpha UV
  64.                 D3DXVECTOR2 colorUV = alphaUV * 8.0f;                                                    //Color UV
  65.  
  66.                 ver[z0 * (width + 1) + x0] = TERRAINVertex(pos, ter.GetNormal(x, z), alphaUV, colorUV);
  67.  
  68.                 //Calculate bounding box bounds...
  69.                 if(pos.x < m_BBox.min.x)m_BBox.min.x = pos.x;
  70.                 if(pos.x > m_BBox.max.x)m_BBox.max.x = pos.x;
  71.                 if(pos.y < m_BBox.min.y)m_BBox.min.y = pos.y;
  72.                 if(pos.y > m_BBox.max.y)m_BBox.max.y = pos.y;
  73.                 if(pos.z < m_BBox.min.z)m_BBox.min.z = pos.z;
  74.                 if(pos.z > m_BBox.max.z)m_BBox.max.z = pos.z;
  75.             }
  76.         m_pMesh->UnlockVertexBuffer();
  77.  
  78.         //Calculate Indices
  79.         WORD* ind = 0;
  80.         m_pMesh->LockIndexBuffer(0,(void**)&ind);    
  81.         int index = 0;
  82.  
  83.         for(int z=source.top, z0 = 0;z<source.bottom;z++, z0++)
  84.             for(int x=source.left, x0 = 0;x<source.right;x++, x0++)
  85.             {
  86.                 //Triangle 1
  87.                 ind[index++] =   z0   * (width + 1) + x0;
  88.                 ind[index++] =   z0   * (width + 1) + x0 + 1;
  89.                 ind[index++] = (z0+1) * (width + 1) + x0;        
  90.  
  91.                 //Triangle 2
  92.                 ind[index++] = (z0+1) * (width + 1) + x0;
  93.                 ind[index++] =   z0   * (width + 1) + x0 + 1;
  94.                 ind[index++] = (z0+1) * (width + 1) + x0 + 1;
  95.             }
  96.  
  97.         m_pMesh->UnlockIndexBuffer();
  98.  
  99.         //Set Attributes
  100.         DWORD *att = 0, a = 0;
  101.         m_pMesh->LockAttributeBuffer(0,&att);
  102.         memset(att, 0, sizeof(DWORD)*nrTri);
  103.         m_pMesh->UnlockAttributeBuffer();
  104.     }
  105.     catch(...)
  106.     {
  107.         debug.Print("Error in PATCH::CreateMesh()");
  108.         return E_FAIL;
  109.     }
  110.  
  111.     return S_OK;
  112. }
  113.  
  114. void PATCH::Render()
  115. {
  116.     //Draw mesh
  117.     if(m_pMesh != NULL)
  118.         m_pMesh->DrawSubset(0);
  119. }
  120.  
  121. //////////////////////////////////////////////////////////////////////////////////////////
  122. //                                    TERRAIN                                                //
  123. //////////////////////////////////////////////////////////////////////////////////////////
  124.  
  125. TERRAIN::TERRAIN()
  126. {
  127.     m_pDevice = NULL;
  128.     m_pMapTiles = NULL;
  129. }
  130.  
  131. void TERRAIN::Init(IDirect3DDevice9* Dev, INTPOINT _size)
  132. {
  133.     m_pDevice = Dev;
  134.     m_size = _size;
  135.     m_pHeightMap = NULL;
  136.     m_updateTerrain = false;
  137.  
  138.     if(m_pMapTiles != NULL)    //Clear old m_pMapTiles
  139.         delete [] m_pMapTiles;
  140.  
  141.     //Create m_pMapTiles
  142.     m_pMapTiles = new MAPTILE[m_size.x * m_size.y];
  143.     memset(m_pMapTiles, 0, sizeof(MAPTILE)*m_size.x*m_size.y);
  144.  
  145.     //Clear old textures
  146.     for(int i=0;i<m_diffuseMaps.size();i++)
  147.         m_diffuseMaps[i]->Release();
  148.     m_diffuseMaps.clear();
  149.  
  150.     //Load textures
  151.     IDirect3DTexture9* grass = NULL, *mount = NULL, *snow = NULL;
  152.     if(FAILED(D3DXCreateTextureFromFile(Dev, "textures/grass.jpg", &grass)))debug.Print("Could not load grass.jpg");
  153.     if(FAILED(D3DXCreateTextureFromFile(Dev, "textures/mountain.jpg", &mount)))debug.Print("Could not load mountain.jpg");
  154.     if(FAILED(D3DXCreateTextureFromFile(Dev, "textures/snow.jpg", &snow)))debug.Print("Could not load snow.jpg");
  155.     m_diffuseMaps.push_back(grass);
  156.     m_diffuseMaps.push_back(mount);
  157.     m_diffuseMaps.push_back(snow);
  158.     m_pAlphaMap = NULL;
  159.     m_pLightMap = NULL;
  160.  
  161.     // Init font
  162.     D3DXCreateFont(m_pDevice, 40, 0, 0, 1, false,  
  163.                    DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY,
  164.                    DEFAULT_PITCH | FF_DONTCARE, "Arial Black", &m_pProgressFont);
  165.  
  166.     //Load pixel & vertex shaders
  167.     m_dirToSun = D3DXVECTOR3(1.0f, 0.6f, 0.5f);
  168.     D3DXVec3Normalize(&m_dirToSun, &m_dirToSun);
  169.  
  170.     m_terrainPS.Init(Dev, "shaders/terrain.ps", PIXEL_SHADER);
  171.     m_terrainVS.Init(Dev, "shaders/terrain.vs", VERTEX_SHADER);
  172.     m_vsMatW = m_terrainVS.GetConstant("matW");
  173.     m_vsMatVP = m_terrainVS.GetConstant("matVP");
  174.     m_vsDirToSun = m_terrainVS.GetConstant("DirToSun");
  175.  
  176.     m_objectsPS.Init(Dev, "shaders/objects.ps", PIXEL_SHADER);
  177.     m_objectsVS.Init(Dev, "shaders/objects.vs", VERTEX_SHADER);
  178.     m_objMatW = m_objectsVS.GetConstant("matW");
  179.     m_objMatVP = m_objectsVS.GetConstant("matVP");
  180.     m_objDirToSun = m_objectsVS.GetConstant("DirToSun");
  181.     m_objMapSize = m_objectsVS.GetConstant("mapSize");
  182.  
  183.     //Create white material    
  184.     m_mtrl.Ambient = m_mtrl.Specular = m_mtrl.Diffuse  = D3DXCOLOR(0.5f, 0.5f, 0.5f, 1.0f);
  185.     m_mtrl.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f);
  186.  
  187.     GenerateRandomTerrain(12);
  188. }
  189.  
  190. void TERRAIN::Release()
  191. {
  192.     for(int i=0;i<m_patches.size();i++)
  193.         if(m_patches[i] != NULL)
  194.             m_patches[i]->Release();
  195.  
  196.     m_patches.clear();
  197.  
  198.     if(m_pHeightMap != NULL)
  199.     {
  200.         m_pHeightMap->Release();
  201.         delete m_pHeightMap;
  202.         m_pHeightMap = NULL;
  203.     }
  204.  
  205.     m_objects.clear();
  206. }
  207.  
  208. void TERRAIN::GenerateRandomTerrain(int numPatches)
  209. {
  210.     try
  211.     {
  212.         Release();
  213.  
  214.         //Create two heightmaps and multiply them
  215.         m_pHeightMap = new HEIGHTMAP(m_size, 20.0f);
  216.         HEIGHTMAP hm2(m_size, 2.0f);
  217.         HEIGHTMAP hm3(m_size, 2.0f);
  218.  
  219.         m_pHeightMap->CreateRandomHeightMap(rand()%2000, 1.0f, 0.7f, 7);
  220.         hm2.CreateRandomHeightMap(rand()%2000, 2.5f, 0.8f, 3);
  221.  
  222.         //Load 4 player filter
  223.         hm3.LoadFromFile(m_pDevice, "heightmaps/four_players.jpg");
  224.  
  225.         hm2.Cap(hm2.m_maxHeight * 0.4f);
  226.  
  227.         *m_pHeightMap *= hm2;
  228.         *m_pHeightMap *= hm3;
  229.         hm2.Release();        
  230.         hm3.Release();
  231.  
  232.         //Add objects
  233.         HEIGHTMAP hm4(m_size, 1.0f);
  234.         hm4.CreateRandomHeightMap(rand()%1000, 5.5f, 0.9f, 7);
  235.  
  236.         for(int y=0;y<m_size.y;y++)
  237.             for(int x=0;x<m_size.x;x++)
  238.             {
  239.                 if(m_pHeightMap->GetHeight(x, y) == 0.0f && hm4.GetHeight(x, y) > 0.7f && rand()%12 == 0)
  240.                     AddObject(0, INTPOINT(x, y));    //Tree
  241.                 else if(m_pHeightMap->GetHeight(x, y) >= 1.0f && hm4.GetHeight(x, y) > 0.9f && rand()%30 == 0)
  242.                     AddObject(1, INTPOINT(x, y));    //Stone
  243.             }
  244.  
  245.         hm4.Release();        
  246.  
  247.         InitPathfinding();
  248.         CreatePatches(numPatches);
  249.         CalculateAlphaMaps();
  250.         CalculateLightMap(true);
  251.     }
  252.     catch(...)
  253.     {
  254.         debug.Print("Error in TERRAIN::GenerateRandomTerrain()");
  255.     }
  256. }
  257.  
  258. void TERRAIN::CreatePatches(int numPatches)
  259. {
  260.     try
  261.     {
  262.         //Clear any old m_patches
  263.         for(int i=0;i<m_patches.size();i++)
  264.             if(m_patches[i] != NULL)
  265.                 m_patches[i]->Release();
  266.         m_patches.clear();
  267.  
  268.         //Create new m_patches
  269.         for(int y=0;y<numPatches;y++)
  270.         {
  271.             Progress("Creating Terrain Mesh", y / (float)numPatches);
  272.  
  273.             for(int x=0;x<numPatches;x++)
  274.             {
  275.                 RECT r = {x * (m_size.x - 1) / (float)numPatches, 
  276.                         y * (m_size.y - 1) / (float)numPatches, 
  277.                         (x+1) * (m_size.x - 1) / (float)numPatches,
  278.                         (y+1) * (m_size.y - 1) / (float)numPatches};
  279.                         
  280.                 PATCH *p = new PATCH();
  281.                 p->CreateMesh(*this, r, m_pDevice);
  282.                 m_patches.push_back(p);
  283.             }
  284.         }
  285.     }
  286.     catch(...)
  287.     {
  288.         debug.Print("Error in TERRAIN::CreatePatches()");
  289.     }
  290. }
  291.  
  292. void TERRAIN::CalculateAlphaMaps()
  293. {
  294.     Progress("Creating Alpha Map", 0.0f);
  295.  
  296.     //Clear old alpha maps
  297.     if(m_pAlphaMap != NULL)
  298.         m_pAlphaMap->Release();
  299.  
  300.     //Create new alpha map
  301.     D3DXCreateTexture(m_pDevice, 128, 128, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pAlphaMap);
  302.  
  303.     //Lock the texture
  304.     D3DLOCKED_RECT sRect;
  305.     m_pAlphaMap->LockRect(0, &sRect, NULL, NULL);
  306.     BYTE *bytes = (BYTE*)sRect.pBits;
  307.     memset(bytes, 0, 128*sRect.Pitch);        //Clear texture to black
  308.  
  309.     for(int i=0;i<m_diffuseMaps.size();i++)
  310.         for(int y=0;y<sRect.Pitch / 4;y++)
  311.             for(int x=0;x<sRect.Pitch / 4;x++)
  312.             {
  313.                 int terrain_x = m_size.x * (x / (float)(sRect.Pitch / 4.0f));
  314.                 int terrain_y = m_size.y * (y / (float)(sRect.Pitch / 4.0f));
  315.                 MAPTILE *tile = GetTile(terrain_x, terrain_y);
  316.  
  317.                 if(tile != NULL && tile->m_type == i)
  318.                     bytes[y * sRect.Pitch + x * 4 + i] = 255;
  319.             }
  320.  
  321.     //Unlock the texture
  322.     m_pAlphaMap->UnlockRect(0);
  323.     
  324.     //D3DXSaveTextureToFile("alpha.bmp", D3DXIFF_BMP, m_pAlphaMap, NULL);
  325. }
  326.  
  327. void TERRAIN::CalculateLightMap(bool allWhite)
  328. {
  329.     try
  330.     {
  331.         //Clear old alpha maps
  332.         if(m_pLightMap != NULL)
  333.             m_pLightMap->Release();
  334.  
  335.         //Create new light map
  336.         D3DXCreateTexture(m_pDevice, 256, 256, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8, D3DPOOL_DEFAULT, &m_pLightMap);
  337.  
  338.         //Lock the texture
  339.         D3DLOCKED_RECT sRect;
  340.         m_pLightMap->LockRect(0, &sRect, NULL, NULL);
  341.         BYTE *bytes = (BYTE*)sRect.pBits;
  342.         memset(bytes, 255, 256*sRect.Pitch);        //Clear texture to white
  343.  
  344.         if(allWhite)
  345.         {
  346.             m_pLightMap->UnlockRect(0);
  347.             return;
  348.         }
  349.  
  350.         for(int y=0;y<sRect.Pitch;y++)
  351.         {
  352.             Progress("Calculating Lightmap", y / (float)sRect.Pitch);
  353.  
  354.             for(int x=0;x<sRect.Pitch;x++)
  355.             {
  356.                 float terrain_x = (float)m_size.x * (x / (float)(sRect.Pitch));
  357.                 float terrain_z = (float)m_size.y * (y / (float)(sRect.Pitch));
  358.  
  359.                 //Find patch that the terrain_x, terrain_z is over
  360.                 bool done = false;
  361.                 for(int p=0;p<m_patches.size() && !done;p++)
  362.                 {
  363.                     RECT mr = m_patches[p]->m_mapRect;
  364.  
  365.                     //Focus within patch maprect or not?
  366.                     if(terrain_x >= mr.left && terrain_x < mr.right &&
  367.                          terrain_z >= mr.top && terrain_z < mr.bottom)
  368.                     {            
  369.                         // Collect only the closest intersection
  370.                         RAY rayTop(D3DXVECTOR3(terrain_x, 10000.0f, -terrain_z), D3DXVECTOR3(0.0f, -1.0f, 0.0f));
  371.                         float dist = rayTop.Intersect(m_patches[p]->m_pMesh);
  372.  
  373.                         if(dist >= 0.0f)
  374.                         {
  375.                             RAY ray(D3DXVECTOR3(terrain_x, 10000.0f - dist + 0.01f, -terrain_z), m_dirToSun);
  376.  
  377.                             for(int p2=0;p2<m_patches.size() && !done;p2++)
  378.                                 if(ray.Intersect(m_patches[p2]->m_BBox) >= 0)
  379.                                 {
  380.                                     if(ray.Intersect(m_patches[p2]->m_pMesh) >= 0)    //In shadow
  381.                                     {
  382.                                         done = true;
  383.                                         bytes[y * sRect.Pitch + x] = 128;
  384.                                     }
  385.                                 }
  386.  
  387.                             done = true;
  388.                         }
  389.                     }
  390.                 }                        
  391.             }
  392.         }
  393.  
  394.         //Smooth lightmap        
  395.         for(int i=0;i<3;i++)
  396.         {
  397.             Progress("Smoothing the Lightmap", i / 3.0f);
  398.  
  399.             BYTE* tmpBytes = new BYTE[sRect.Pitch * sRect.Pitch];
  400.             memcpy(tmpBytes, sRect.pBits, sRect.Pitch * sRect.Pitch);
  401.  
  402.             for(int y=1;y<sRect.Pitch-1;y++)
  403.                 for(int x=1;x<sRect.Pitch-1;x++)
  404.                 {
  405.                     long index = y*sRect.Pitch + x;
  406.                     BYTE b1 = bytes[index];
  407.                     BYTE b2 = bytes[index - 1];
  408.                     BYTE b3 = bytes[index - sRect.Pitch];
  409.                     BYTE b4 = bytes[index + 1];
  410.                     BYTE b5 = bytes[index + sRect.Pitch];
  411.                     
  412.                     tmpBytes[index] = (BYTE)((b1 + b2 + b3 + b4 + b5) / 5);
  413.                 }
  414.  
  415.             memcpy(sRect.pBits, tmpBytes, sRect.Pitch * sRect.Pitch);
  416.             delete [] tmpBytes;
  417.         }
  418.  
  419.         //Unlock the texture
  420.         m_pLightMap->UnlockRect(0);
  421.         
  422.         //D3DXSaveTextureToFile("light.bmp", D3DXIFF_BMP, m_pLightMap, NULL);
  423.     }
  424.     catch(...)
  425.     {
  426.         debug.Print("Error in TERRAIN::CalculateLightMap()");
  427.     }
  428. }
  429.  
  430. D3DXVECTOR3 TERRAIN::GetNormal(int x, int y)
  431. {
  432.     //Neighboring map nodes (D, B, C, F, H, G)
  433.     INTPOINT mp[] = {INTPOINT(x-1, y),   INTPOINT(x, y-1), 
  434.                      INTPOINT(x+1, y-1), INTPOINT(x+1, y),
  435.                        INTPOINT(x, y+1),   INTPOINT(x-1, y+1)};
  436.  
  437.     //if there's an invalid map node return (0, 1, 0)
  438.     if(!Within(mp[0]) || !Within(mp[1]) || !Within(mp[2]) || 
  439.        !Within(mp[3]) || !Within(mp[4]) || !Within(mp[5]))
  440.         return D3DXVECTOR3(0.0f, 1.0f, 0.0f);
  441.  
  442.     //Calculate the normals of the 6 neighboring planes
  443.     D3DXVECTOR3 normal = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
  444.  
  445.     for(int i=0;i<6;i++)
  446.     {
  447.         D3DXPLANE plane;
  448.         D3DXPlaneFromPoints(&plane, 
  449.                             &GetWorldPos(INTPOINT(x, y)),
  450.                             &GetWorldPos(mp[i]), 
  451.                             &GetWorldPos(mp[(i + 1) % 6]));
  452.  
  453.         normal +=  D3DXVECTOR3(plane.a, plane.b, plane.c);
  454.     }
  455.  
  456.     D3DXVec3Normalize(&normal, &normal);
  457.     return normal;
  458. }
  459.  
  460. void TERRAIN::AddObject(int type, INTPOINT mappos)
  461. {
  462.     D3DXVECTOR3 pos = D3DXVECTOR3(mappos.x, m_pHeightMap->GetHeight(mappos), -mappos.y);    
  463.     D3DXVECTOR3 rot = D3DXVECTOR3((rand()%1000 / 1000.0f) * 0.13f, (rand()%1000 / 1000.0f) * 3.0f, (rand()%1000 / 1000.0f) * 0.13);
  464.  
  465.     float sca_xz = (rand()%1000 / 1000.0f) * 0.5f + 0.5f;
  466.     float sca_y = (rand()%1000 / 1000.0f) * 1.0f + 0.5f;
  467.     D3DXVECTOR3 sca = D3DXVECTOR3(sca_xz, sca_y, sca_xz);
  468.  
  469.     m_objects.push_back(OBJECT(type, mappos, pos, rot, sca));
  470. }
  471.  
  472. void TERRAIN::Render(CAMERA &camera)
  473. {
  474.     if(m_updateTerrain)
  475.     {
  476.         m_updateTerrain = false;        
  477.         CreatePatches(12);
  478.         CalculateAlphaMaps();
  479.         CalculateLightMap(true);
  480.     }
  481.  
  482.     //Set render states        
  483.     m_pDevice->SetRenderState(D3DRS_LIGHTING, false);
  484.     m_pDevice->SetRenderState(D3DRS_ZWRITEENABLE, true);    
  485.     
  486.     m_pDevice->SetTexture(0, m_pAlphaMap);
  487.     m_pDevice->SetTexture(1, m_diffuseMaps[0]);        //Grass
  488.     m_pDevice->SetTexture(2, m_diffuseMaps[1]);        //Mountain
  489.     m_pDevice->SetTexture(3, m_diffuseMaps[2]);        //Snow
  490.     m_pDevice->SetTexture(4, m_pLightMap);            //Lightmap
  491.     m_pDevice->SetMaterial(&m_mtrl);
  492.  
  493.     D3DXMATRIX world, vp = camera.GetViewMatrix() * camera.GetProjectionMatrix();
  494.     D3DXMatrixIdentity(&world);
  495.     m_pDevice->SetTransform(D3DTS_WORLD, &world);
  496.     
  497.     //Set vertex shader variables
  498.     m_terrainVS.SetMatrix(m_vsMatW, world);
  499.     m_terrainVS.SetMatrix(m_vsMatVP, vp);
  500.     m_terrainVS.SetVector3(m_vsDirToSun, m_dirToSun);
  501.  
  502.     m_terrainVS.Begin();
  503.     m_terrainPS.Begin();
  504.         
  505.     for(int p=0;p<m_patches.size();p++)
  506.         if(!camera.Cull(m_patches[p]->m_BBox))
  507.             m_patches[p]->Render();
  508.  
  509.     m_terrainPS.End();
  510.     m_terrainVS.End();
  511.  
  512.     m_pDevice->SetTexture(1, NULL);
  513.     m_pDevice->SetTexture(2, NULL);
  514.     m_pDevice->SetTexture(3, NULL);
  515.     m_pDevice->SetTexture(4, NULL);
  516.  
  517.     //Render Objects
  518.     m_objectsVS.SetMatrix(m_objMatW, world);
  519.     m_objectsVS.SetMatrix(m_objMatVP, vp);
  520.     m_objectsVS.SetVector3(m_objDirToSun, m_dirToSun);
  521.     m_objectsVS.SetVector3(m_objMapSize, D3DXVECTOR3(m_size.x, m_size.y, 0.0f));
  522.  
  523.     m_pDevice->SetTexture(1, m_pLightMap);        //Lightmap
  524.     
  525.     m_objectsVS.Begin();
  526.     m_objectsPS.Begin();
  527.  
  528.     for(int i=0;i<m_objects.size();i++)
  529.         if(!camera.Cull(m_objects[i].m_BBox))
  530.         {
  531.             D3DXMATRIX m = m_objects[i].m_meshInstance.GetWorldMatrix();
  532.             m_objectsVS.SetMatrix(m_objMatW, m);
  533.             m_objects[i].Render();
  534.         }
  535.  
  536.     m_objectsVS.End();
  537.     m_objectsPS.End();
  538. }
  539.  
  540. void TERRAIN::Progress(std::string text, float prc)
  541. {
  542.     m_pDevice->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0L);
  543.     m_pDevice->BeginScene();
  544.     
  545.     RECT rc = {200, 250, 600, 300};
  546.     m_pProgressFont->DrawText(NULL, text.c_str(), -1, &rc, DT_CENTER | DT_VCENTER | DT_NOCLIP, 0xff000000);
  547.  
  548.     //Progress bar
  549.     D3DRECT r;
  550.     r.x1 = 200; r.x2 = 600;
  551.     r.y1 = 300; r.y2 = 340;
  552.     m_pDevice->Clear(1, &r, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xff000000, 1.0f, 0L);
  553.     r.x1 = 202; r.x2 = 598;
  554.     r.y1 = 302; r.y2 = 338;
  555.     m_pDevice->Clear(1, &r, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0L);
  556.     r.x1 = 202; r.x2 = 202 + 396 * prc;
  557.     r.y1 = 302; r.y2 = 338;
  558.     m_pDevice->Clear(1, &r, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xff00ff00, 1.0f, 0L);
  559.  
  560.     m_pDevice->EndScene();
  561.     m_pDevice->Present(0, 0, 0, 0);
  562. }
  563.  
  564. bool TERRAIN::Within(INTPOINT p)
  565. {
  566.     return p.x >= 0 && p.y >= 0 && p.x < m_size.x && p.y < m_size.y;
  567. }
  568.  
  569. void TERRAIN::InitPathfinding()
  570. {
  571.     try
  572.     {
  573.         //Read maptile heights & types from heightmap
  574.         for(int y=0;y<m_size.y;y++)
  575.             for(int x=0;x<m_size.x;x++)
  576.             {
  577.                 MAPTILE *tile = GetTile(x, y);
  578.                 if(m_pHeightMap != NULL)tile->m_height = m_pHeightMap->GetHeight(x, y);
  579.                 tile->m_mappos = INTPOINT(x, y);
  580.                 
  581.                 if(tile->m_height < 0.3f)         tile->m_type = 0;    //Grass
  582.                 else if(tile->m_height < 7.0f) tile->m_type = 1;    //Stone
  583.                 else                         tile->m_type = 2;    //Snow
  584.             }
  585.  
  586.         //Calculate tile cost as a function of the height variance
  587.         for(int y=0;y<m_size.y;y++)        
  588.             for(int x=0;x<m_size.x;x++)
  589.             {
  590.                 MAPTILE *tile = GetTile(x, y);
  591.  
  592.                 if(tile != NULL)
  593.                 {
  594.                     //Possible neighbors
  595.                     INTPOINT p[] = {INTPOINT(x-1, y-1), INTPOINT(x, y-1), INTPOINT(x+1, y-1),
  596.                                     INTPOINT(x-1, y),                      INTPOINT(x+1, y),
  597.                                     INTPOINT(x-1, y+1), INTPOINT(x, y+1), INTPOINT(x+1, y+1)};
  598.  
  599.                     float variance = 0.0f;
  600.                     int nr = 0;
  601.  
  602.                     //For each neighbor
  603.                     for(int i=0;i<8;i++)    
  604.                         if(Within(p[i]))
  605.                         {
  606.                             MAPTILE *neighbor = GetTile(p[i]);
  607.  
  608.                             if(neighbor != NULL)
  609.                             {
  610.                                 float v = neighbor->m_height - tile->m_height;
  611.                                 variance += (v * v);
  612.                                 nr++;
  613.                             }
  614.                         }
  615.  
  616.                     //Cost = height variance
  617.                     variance /= (float)nr;
  618.                     tile->m_cost = variance + 0.1f;
  619.                     if(tile->m_cost > 1.0f)tile->m_cost = 1.0f;
  620.  
  621.                     //If the tile cost is less than 1.0f, then we can walk on the tile
  622.                     tile->m_walkable = tile->m_cost < 0.5f;
  623.                 }
  624.             }
  625.  
  626.         //Make maptiles with objects on them not m_walkable
  627.         for(int i=0;i<m_objects.size();i++)
  628.         {
  629.             MAPTILE *tile = GetTile(m_objects[i].m_mappos);
  630.             if(tile != NULL)
  631.             {
  632.                 tile->m_walkable = false;
  633.                 tile->m_cost = 1.0f;
  634.             }
  635.         }
  636.  
  637.         //Connect m_pMapTiles using the neightbors[] pointers
  638.         for(int y=0;y<m_size.y;y++)        
  639.             for(int x=0;x<m_size.x;x++)
  640.             {
  641.                 MAPTILE *tile = GetTile(x, y);
  642.                 if(tile != NULL && tile->m_walkable)
  643.                 {
  644.                     //Clear old connections
  645.                     for(int i=0;i<8;i++)
  646.                         tile->m_pNeightbors[i] = NULL;
  647.  
  648.                     //Possible neighbors
  649.                     INTPOINT p[] = {INTPOINT(x-1, y-1), INTPOINT(x, y-1), INTPOINT(x+1, y-1),
  650.                                     INTPOINT(x-1, y),                      INTPOINT(x+1, y),
  651.                                     INTPOINT(x-1, y+1), INTPOINT(x, y+1), INTPOINT(x+1, y+1)};
  652.  
  653.                     //For each neighbor
  654.                     for(int i=0;i<8;i++)    
  655.                         if(Within(p[i]))
  656.                         {
  657.                             MAPTILE *neighbor = GetTile(p[i]);
  658.  
  659.                             //Connect tiles if the neighbor is walkable
  660.                             if(neighbor != NULL && neighbor->m_walkable)
  661.                                 tile->m_pNeightbors[i] = neighbor;
  662.                         }
  663.                 }
  664.             }
  665.  
  666.         CreateTileSets();
  667.     }
  668.     catch(...)
  669.     {
  670.         debug.Print("Error in InitPathfinding()");
  671.     }    
  672. }
  673.  
  674. void TERRAIN::UpdatePathfinding(RECT *r)
  675. {
  676.     if(r == NULL)
  677.     {
  678.         InitPathfinding();
  679.         return;
  680.     }
  681.  
  682.     //Connect maptiles using the neightbors[] pointers
  683.     for(int y=r->top; y<=r->bottom; y++)        
  684.         for(int x=r->left; x<=r->right; x++)
  685.         {
  686.             MAPTILE *tile = GetTile(x, y);
  687.             if(tile != NULL && tile->m_walkable)
  688.             {
  689.                 //Clear old connections
  690.                 for(int i=0;i<8;i++)
  691.                     tile->m_pNeightbors[i] = NULL;
  692.  
  693.                 //Possible neighbors
  694.                 INTPOINT p[] = {INTPOINT(x-1, y-1), INTPOINT(x, y-1), INTPOINT(x+1, y-1),
  695.                                 INTPOINT(x-1, y),                      INTPOINT(x+1, y),
  696.                                 INTPOINT(x-1, y+1), INTPOINT(x, y+1), INTPOINT(x+1, y+1)};
  697.  
  698.                 //For each neighbor
  699.                 for(int i=0;i<8;i++)
  700.                     if(Within(p[i]))
  701.                     {
  702.                         MAPTILE *neighbor = GetTile(p[i]);
  703.  
  704.                         //Connect tiles if the neighbor is walkable
  705.                         if(neighbor != NULL && neighbor->m_walkable)
  706.                             tile->m_pNeightbors[i] = neighbor;
  707.                     }
  708.             }
  709.         }
  710.  
  711.     CreateTileSets();
  712. }
  713.  
  714. void TERRAIN::CreateTileSets()
  715. {
  716.     try
  717.     {
  718.         int setNo = 0;
  719.         for(int y=0;y<m_size.y;y++)        //Set a unique set for each tile...
  720.             for(int x=0;x<m_size.x;x++)
  721.                 m_pMapTiles[x + y * m_size.x].m_set = setNo++;
  722.  
  723.         bool changed = true;
  724.         while(changed)
  725.         {
  726.             changed = false;
  727.  
  728.             for(int y=0;y<m_size.y;y++)
  729.                 for(int x=0;x<m_size.x;x++)
  730.                 {
  731.                     MAPTILE *tile = GetTile(x, y);
  732.  
  733.                     //Find the lowest m_set of a neighbor
  734.                     if(tile != NULL && tile->m_walkable)
  735.                     {
  736.                         for(int i=0;i<8;i++)
  737.                             if(tile->m_pNeightbors[i] != NULL &&
  738.                                 tile->m_pNeightbors[i]->m_walkable &&
  739.                                 tile->m_pNeightbors[i]->m_set < tile->m_set)
  740.                             {
  741.                                 changed = true;
  742.                                 tile->m_set = tile->m_pNeightbors[i]->m_set;
  743.                             }
  744.                     }
  745.                 }
  746.         }
  747.     }
  748.     catch(...)
  749.     {
  750.         debug.Print("Error in TERRAIN::CreateTileSets()");
  751.     }
  752. }
  753.  
  754. float H(INTPOINT a, INTPOINT b)
  755. {
  756.     //return abs(a.x - b.x) + abs(a.y - b.y);
  757.     return a.Distance(b);
  758. }
  759.  
  760. std::vector<INTPOINT> TERRAIN::GetPath(INTPOINT start, INTPOINT goal, bool considerUnits)
  761. {
  762.     try
  763.     {
  764.         //Check that the two points are within the bounds of the map
  765.         MAPTILE *startTile = GetTile(start);
  766.         MAPTILE *goalTile = GetTile(goal);
  767.  
  768.         if(!Within(start) || !Within(goal) || start == goal || startTile == NULL || goalTile == NULL)
  769.             return std::vector<INTPOINT>();
  770.  
  771.         //Check if a path exists
  772.         if(!startTile->m_walkable || !goalTile->m_walkable || startTile->m_set != goalTile->m_set)
  773.             return std::vector<INTPOINT>();
  774.  
  775.         //Init Search
  776.         long numTiles = m_size.x * m_size.y;
  777.         for(long l=0;l<numTiles;l++)
  778.         {
  779.             m_pMapTiles[l].f = m_pMapTiles[l].g = INT_MAX;        //Clear F,G
  780.             m_pMapTiles[l].open = m_pMapTiles[l].closed = false;    //Reset Open and Closed
  781.         }
  782.  
  783.         std::vector<MAPTILE*> open;                //Create Our Open list
  784.         startTile->g = 0;                        //Init our starting point (SP)
  785.         startTile->f = H(start, goal);
  786.         startTile->open = true;
  787.         open.push_back(startTile);                //Add SP to the Open list
  788.  
  789.         bool found = false;                    // Search as long as a path hasnt been found,
  790.         while(!found && !open.empty())        // or there is no more tiles to search
  791.         {                                                
  792.             MAPTILE * best = open[0];        // Find the best tile (i.e. the lowest F value)
  793.             int bestPlace = 0;
  794.             for(int i=1;i<open.size();i++)
  795.                 if(open[i]->f < best->f)
  796.                 {
  797.                     best = open[i];
  798.                     bestPlace = i;
  799.                 }
  800.             
  801.             if(best == NULL)break;            //No path found
  802.  
  803.             open[bestPlace]->open = false;
  804.             open.erase(&open[bestPlace]);    // Take the best node out of the Open list
  805.  
  806.             if(best->m_mappos == goal)        //If the goal has been found
  807.             {
  808.                 std::vector<INTPOINT> p, p2;
  809.                 MAPTILE *point = best;
  810.  
  811.                 while(point->m_mappos != start)    // Generate path
  812.                 {
  813.                     p.push_back(point->m_mappos);
  814.                     point = point->m_pParent;
  815.                 }
  816.  
  817.                 for(int i=p.size()-1;i!=0;i--)    // Reverse path
  818.                     p2.push_back(p[i]);
  819.                 p2.push_back(goal);
  820.                 return p2;
  821.             }
  822.             else
  823.             {
  824.                 for(i=0;i<8;i++)                    // otherwise, check the neighbors of the
  825.                     if(best->m_pNeightbors[i] != NULL)    // best tile
  826.                         if(!considerUnits || best->m_pNeightbors[i]->m_pMapObject == NULL)
  827.                         {
  828.                             bool inList = false;        // Generate new G and F value
  829.                             float newG = best->g + 1.0f;
  830.                             float d = H(best->m_mappos, best->m_pNeightbors[i]->m_mappos);
  831.                             float newF = newG + H(best->m_pNeightbors[i]->m_mappos, goal) + best->m_pNeightbors[i]->m_cost * 5.0f * d;
  832.  
  833.                             if(best->m_pNeightbors[i]->open || best->m_pNeightbors[i]->closed)
  834.                             {
  835.                                 if(newF < best->m_pNeightbors[i]->f)    // If the new F value is lower
  836.                                 {
  837.                                     best->m_pNeightbors[i]->g = newG;    // update the values of this tile
  838.                                     best->m_pNeightbors[i]->f = newF;
  839.                                     best->m_pNeightbors[i]->m_pParent = best;                                
  840.                                 }
  841.                                 inList = true;
  842.                             }
  843.  
  844.                             if(!inList)            // If the neighbor tile isn't in the Open or Closed list
  845.                             {
  846.                                 best->m_pNeightbors[i]->f = newF;        //Set the values
  847.                                 best->m_pNeightbors[i]->g = newG;
  848.                                 best->m_pNeightbors[i]->m_pParent = best;
  849.                                 best->m_pNeightbors[i]->open = true;
  850.                                 open.push_back(best->m_pNeightbors[i]);    //Add it to the open list    
  851.                             }
  852.                         }
  853.  
  854.                 best->closed = true;        //The best tile has now been searched, add it to the Closed list
  855.             }
  856.         }
  857.  
  858.         return std::vector<INTPOINT>();        //No path found, return an empty path
  859.         
  860.     }
  861.     catch(...)
  862.     {
  863.         debug.Print("Error in TERRAIN::GetPath()");
  864.         return std::vector<INTPOINT>();
  865.     }
  866. }
  867.  
  868. MAPTILE* TERRAIN::GetTile(int x, int y)
  869. {
  870.     if(m_pMapTiles == NULL)return NULL;
  871.  
  872.     try
  873.     {
  874.         return &m_pMapTiles[x + y * m_size.x];
  875.     }
  876.     catch(...)
  877.     {
  878.         return NULL;
  879.     }
  880. }
  881.  
  882. void TERRAIN::SaveTerrain(char fileName[])
  883. {
  884.     try
  885.     {
  886.         std::ofstream out(fileName, std::ios::binary);        //Binary format
  887.  
  888.         if(out.good())
  889.         {
  890.             out.write((char*)&m_size, sizeof(INTPOINT));    //Write map size
  891.  
  892.             //Write all the maptile information needed to recreate the map
  893.             for(int y=0;y<m_size.y;y++)
  894.                 for(int x=0;x<m_size.x;x++)
  895.                 {
  896.                     MAPTILE *tile = GetTile(x, y);
  897.                     out.write((char*)&tile->m_type, sizeof(int));            //type
  898.                     out.write((char*)&tile->m_height, sizeof(float));        //Height
  899.                 }
  900.  
  901.             //Write all the objects
  902.             int numObjects = m_objects.size();
  903.             out.write((char*)&numObjects, sizeof(int));     //Num Objects
  904.             for(int i=0;i<m_objects.size();i++)
  905.             {
  906.                 out.write((char*)&m_objects[i].m_type, sizeof(int));                //type
  907.                 out.write((char*)&m_objects[i].m_mappos, sizeof(INTPOINT));            //mappos
  908.                 out.write((char*)&m_objects[i].m_meshInstance.m_pos, sizeof(D3DXVECTOR3));    //Pos
  909.                 out.write((char*)&m_objects[i].m_meshInstance.m_rot, sizeof(D3DXVECTOR3));    //Rot
  910.                 out.write((char*)&m_objects[i].m_meshInstance.m_sca, sizeof(D3DXVECTOR3));    //Sca
  911.             }
  912.         }
  913.  
  914.         out.close();
  915.     }
  916.     catch(...)
  917.     {
  918.         debug.Print("Error in TERRAIN::SaveTerrain()");
  919.     }
  920. }
  921.  
  922. void TERRAIN::LoadTerrain(char fileName[])
  923. {
  924.     try
  925.     {
  926.         std::ifstream in(fileName, std::ios::binary);        //Binary format
  927.  
  928.         if(in.good())
  929.         {
  930.             Release();    //Release all terrain resources
  931.  
  932.             in.read((char*)&m_size, sizeof(INTPOINT));    //read map size
  933.         
  934.             if(m_pMapTiles != NULL)    //Clear old m_pMapTiles
  935.                 delete [] m_pMapTiles;
  936.  
  937.             //Create new maptiles
  938.             m_pMapTiles = new MAPTILE[m_size.x * m_size.y];
  939.             memset(m_pMapTiles, 0, sizeof(MAPTILE)*m_size.x*m_size.y);
  940.  
  941.  
  942.             //Read the maptile information
  943.             for(int y=0;y<m_size.y;y++)
  944.                 for(int x=0;x<m_size.x;x++)
  945.                 {
  946.                     MAPTILE *tile = GetTile(x, y);
  947.                     in.read((char*)&tile->m_type, sizeof(int));            //type
  948.                     in.read((char*)&tile->m_height, sizeof(float));        //Height
  949.                 }
  950.  
  951.             //Read number of objects
  952.             int numObjects = 0;
  953.             in.read((char*)&numObjects, sizeof(int));
  954.             for(int i=0;i<numObjects;i++)
  955.             {
  956.                 int type = 0;
  957.                 INTPOINT mp;
  958.                 D3DXVECTOR3 p, r, s;
  959.  
  960.                 in.read((char*)&type, sizeof(int));            //type
  961.                 in.read((char*)&mp, sizeof(INTPOINT));        //mappos
  962.                 in.read((char*)&p, sizeof(D3DXVECTOR3));    //Pos
  963.                 in.read((char*)&r, sizeof(D3DXVECTOR3));    //Rot
  964.                 in.read((char*)&s, sizeof(D3DXVECTOR3));    //Sca
  965.  
  966.                 m_objects.push_back(OBJECT(type, mp, p, r, s));
  967.             }
  968.  
  969.             //Recreate Terrain
  970.             InitPathfinding();
  971.             CreatePatches(3);
  972.             CalculateAlphaMaps();
  973.             CalculateLightMap(true);
  974.         }
  975.  
  976.         in.close();
  977.     }
  978.     catch(...)
  979.     {
  980.         debug.Print("Error in TERRAIN::LoadTerrain()");
  981.     }
  982. }
  983.  
  984. D3DXVECTOR3 TERRAIN::GetWorldPos(INTPOINT mappos)
  985. {
  986.     if(!Within(mappos))return D3DXVECTOR3(0, 0, 0);
  987.     MAPTILE *tile = GetTile(mappos);
  988.     return D3DXVECTOR3(mappos.x, tile->m_height, -mappos.y);
  989. }
  990.  
  991. INTPOINT TERRAIN::GetClosestFreeTile(INTPOINT to, INTPOINT from)
  992. {
  993.     for(int i=0;i<5;i++)    //Search radius
  994.     {
  995.         std::vector<MAPTILE*> tiles;
  996.  
  997.         //Get tiles in current search radius
  998.         for(int y=to.y - i;y<=to.y + i;y++)
  999.         {
  1000.             tiles.push_back(GetTile(to.x - i, y));
  1001.             tiles.push_back(GetTile(to.x + i, y));
  1002.         }
  1003.  
  1004.         for(int x=to.x - i + 1;x<=to.x + i - 1;x++)
  1005.         {
  1006.             tiles.push_back(GetTile(x, to.y - i));
  1007.             tiles.push_back(GetTile(x, to.y + i));
  1008.         }
  1009.  
  1010.         //Find closest tile...
  1011.         MAPTILE *closest = NULL;
  1012.         float dist = 10000.0f;
  1013.  
  1014.         for(int t=0;t<tiles.size();t++)
  1015.             if(tiles[t] != NULL)
  1016.                 if(tiles[t]->m_walkable && tiles[t]->m_pMapObject == NULL)
  1017.                 {
  1018.                     float d = from.Distance(tiles[t]->m_mappos);
  1019.                     if(d < dist)
  1020.                     {
  1021.                         dist = d;
  1022.                         closest = tiles[t];
  1023.                     }
  1024.                 }
  1025.  
  1026.         if(closest != NULL)
  1027.             return closest->m_mappos;
  1028.     }
  1029.  
  1030.     return to;
  1031. }